/*
 * client.cpp
 *
 *  Created on: 18.08.2014
 *      Author: Dave
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <cstdio>
#include <string>
#include <unistd.h>
#include <iostream>
#include <mqueue.h>
#include "IOUnit.h"

#define UDS_FILE "/tmp/sock.uds"
#define RND_FILE "rndout.bin"

static const int TCP_CODE = 0x02;
static const int UDP_CODE = 0x04;
static const int UDS_CODE = 0x08;
static const int MQ_CODE = 0x10;


int param_count = 0;
int bitCount = 0;
IOUnit* io;
char* ip_addr;


int readParameters(int param_count, char** params, int* new_count)
{
	int code = 0;
	int temp  = param_count;


	for(int i = 1; i < param_count; i++)
	{
		if(strcmp(params[i], "-bbs") == 0)
		{
			i++;
			if(i < param_count)
			{
				sscanf(params[i], "%d", &bitCount);
				if(bitCount <= 0)
				{
					std::cout << "No number of bits to generate where given.\n";
					return -1;
				}
			}
			else
			{
				std::cout << "No number of bits to generate where given.\n";
				return -1;
			}
		}
		else if(strcmp(params[i], "-ip") == 0)
		{
			i++;
			ip_addr = params[i];
			temp-=2;
		}
		else if(strcmp(params[i], "-i") == 0)
		{
			memset(params[i], 0, strlen(params[i]));
			i++;
			if(i < param_count && strcmp(params[i], ""))
			{
				if(i < param_count && (strcmp(params[i], "tcp") == 0))
				{
					//bit fuer tcp setzen
					code |= TCP_CODE;
					memset(params[i], '\0', strlen(params[i]));
					temp-=2;
				}
				else if(i < param_count && (strcmp(params[i], "udp") == 0))
				{
					code |= UDP_CODE;
					memset(params[i], '\0', strlen(params[i]));
					temp-=2;
				}
				else if(i < param_count && (strcmp(params[i], "uds") == 0))
				{
					code |= UDS_CODE;
					memset(params[i], '\0', strlen(params[i]));
					temp-=2;
				}
				else if(i < param_count && (strcmp(params[i], "mq") == 0))
				{
					code |= MQ_CODE;
					memset(params[i], '\0', strlen(params[i]));
					temp-=2;
				}
			}
		}
		// show help
		else if(strcmp(params[i], "--help") == 0)
		{
			std::cout << "\nAufruf: prngd_client OPTION..."
			<< "\nSendet einen Befehl zur Erzeugung von Zufallsbits an prng_server\ns"
			<< "und speichert die Antwort binär in der Datei rndout.bin\n"
			<< "\n OPTION:"
			<< "\n -bbs xxxx \t bevorzugt Blum Blum Shub Generator verwenden und xxxx Zufallsbits generieren. "
			<< "\n -fbbs xxxx \ŧ bevorzugt BBS mit Blockgröße von 248 Bit verwenden.\n"
			<< "\n -xxx \t bevorzugt den anderen Generator verwenden.\n"
			<< "\n -s xxxx \t weißt prngd_server an die Eingabe xxxx zur Generierung des Seeds zu verwenden."
			<< "\n -i xxx \t Aktiviert Interface xxx, wobei xxx = tcp, udp, uds, mq order local sein kann."
			<< "\n --help \t zeigt diese Hilfe an.\n\n";
		}
	}
	*new_count = temp;
	return code;

}

void testTcp(int param_count, char** params)
{
	char* recv_buffer = NULL;
	std::string* send_buffer = new std::string();
	bool nothing_recv = true;

	struct sockaddr_in serv_addr;
	int size = 0;
	int create_socket = 0;
	int recv_size = bitCount/8 + bitCount%8;

	if(recv_size > 0)
		recv_buffer = new char[recv_size];

	//put all params into one array
	for(int i = 1; i < param_count; i++)
	{
		send_buffer->append(params[i]);
		send_buffer->append(" ");
	}
	send_buffer->erase(send_buffer->end()-1, send_buffer->end());
	send_buffer->append("\0");

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(18200);
	inet_aton(ip_addr, &serv_addr.sin_addr);


	if((create_socket=socket(AF_INET, SOCK_STREAM, 0)) > 0)
		std::cout << "\n Tcp Socket wurde angelegt\n";

	if(connect(create_socket, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == 0)
	{
		std::cout << "Verbindung mit Server hergestellt\n";
		std::cout << "Sende Nachricht: " << send_buffer->c_str() << "\n";
		send(create_socket, send_buffer->c_str(), send_buffer->length(), 0);

		if(strcmp(send_buffer->c_str(), "/exit") == 0)
			nothing_recv = false;

		while(nothing_recv)
		{
			size = recv(create_socket, recv_buffer, recv_size, 0);
			if(size > 0)
			{
				for(int i = 0; i < recv_size; i++)
				{
					std::cout << recv_buffer[i];
				}
				nothing_recv = false;
			}
			io->writeToFile(RND_FILE, recv_buffer, recv_size, true);
		}
		close(create_socket);

	}

	if(recv_buffer != NULL)
		delete recv_buffer;

	delete send_buffer;

}


void testUdp(int param_count, char** params)
{
	char* recv_buffer = NULL;
	std::string* send_buffer = new std::string();
	bool nothing_recv = true;

	struct sockaddr_in serv_addr;
	socklen_t serv_addr_len = sizeof(serv_addr);
	int size = 0;
	int create_socket = 0;

	int recv_size = bitCount/8 + bitCount%8;

	if(recv_size > 0)
		recv_buffer = new char[recv_size];

	//put all params into one array
	for(int i = 1; i < param_count; i++)
	{
		send_buffer->append(params[i]);
		send_buffer->append(" ");
	}
	send_buffer->erase(send_buffer->end()-1, send_buffer->end());
	send_buffer->append("\0");

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(18201);
	inet_aton(ip_addr, &serv_addr.sin_addr);

	if((create_socket=socket(AF_INET, SOCK_DGRAM, 0)) > 0)
		std::cout << "\nSocket wurde angelegt\n";

	bind(create_socket, (struct sockaddr*)&serv_addr, sizeof(serv_addr));


	size = sendto(create_socket, send_buffer->c_str(), send_buffer->length(), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
	if(size > 0)
		std::cout << "\n Nachricht wurde gesendet.\n";
	else
		std::cout << "\n Fehler beim Senden\n";

	if(strcmp(send_buffer->c_str(), "/exit") == 0)
				nothing_recv = false;

	while(nothing_recv)
	{
		size = recvfrom(create_socket, recv_buffer, recv_size, 0, (struct sockaddr*)&serv_addr, &serv_addr_len);

		if(size > 0)
		{
			std::cout << "\n Empfangen: ";

			for(int i = 0; i < recv_size; i++)
				std::cout << recv_buffer[i];

			nothing_recv = false;
		}
		io->writeToFile(RND_FILE, recv_buffer, recv_size, true);
	}
	close(create_socket);

	if(recv_buffer != NULL)
		delete recv_buffer;
	delete send_buffer;
}


void testUds(int param_count, char** params)
{
	char* recv_buffer = NULL;
	std::string* send_buffer = new std::string();
	bool nothing_recv = true;

	struct sockaddr_un serv_addr;
	int size = 0;
	int create_socket = 0;

	int recv_size = bitCount/8 + bitCount%8;

	if(recv_size > 0)
		recv_buffer = new char[recv_size];

	//put all params into one array
	for(int i = 1; i < param_count; i++)
	{
		send_buffer->append(params[i]);
		send_buffer->append(" ");
	}
	send_buffer->erase(send_buffer->end()-1, send_buffer->end());
	send_buffer->append("\0");


	if((create_socket=socket(AF_UNIX, SOCK_STREAM, 0)) > 0)
		std::cout << "\n Uds Socket wurde angelegt\n";

	serv_addr.sun_family = AF_UNIX;
	strcpy(serv_addr.sun_path, UDS_FILE);


	if(connect(create_socket, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == 0)
	{
		std::cout << "Verbindung mit Server hergestellt\n";
		std::cout << "Sende Nachricht: " << send_buffer->c_str() << "\n";
		send(create_socket, send_buffer->c_str(), send_buffer->length(), 0);

		if(strcmp(send_buffer->c_str(), "/exit") == 0)
			nothing_recv = false;

		while(nothing_recv)
		{
			size = recv(create_socket, recv_buffer, recv_size,0);
			if(size > 0)
			{
				for(int i = 0; i < recv_size; i++)
				{
					std::cout << recv_buffer[i];
				}
				nothing_recv = false;
			}
			io->writeToFile(RND_FILE, recv_buffer, recv_size, true);
		}
		close(create_socket);

	}
	if(recv_buffer != NULL)
		delete recv_buffer;
	delete send_buffer;

}


void testMq(int param_count, char** params)
{
	mqd_t mqs, mqc;
	struct mq_attr attr;
	std::string* send_buffer = new std::string();
	char* recv_buffer = NULL;
	int size = 0;
	bool nothing_recv = true;

	int recv_size = bitCount/8 + bitCount%8;

	if(recv_size > 0)
		recv_buffer = new char[8192];

	//put all params into one array
	for(int i = 1; i < param_count; i++)
	{
		send_buffer->append(params[i]);
		send_buffer->append(" ");
	}
	send_buffer->erase(send_buffer->end()-1, send_buffer->end());
	send_buffer->append("\0");

	std::cout << "\nReceive_size : " << recv_size;


	//client queue read only
	mqc = mq_open("/prngdcq", O_RDONLY);

	//Server queue write only
	mqs = mq_open("/prngdsq", O_WRONLY);


	mq_send(mqs, send_buffer->c_str(), send_buffer->length(), 0);

	if(strcmp(send_buffer->c_str(), "/exit") == 0)
		nothing_recv = false;

	while(nothing_recv)
	{
		sleep(2);
		std::cout << "\nWarte...";
		size = mq_receive(mqc, recv_buffer, 8192, 0);
		if(size > 0)
		{
			std::cout << "\nVon Server: ";
			for(int i = 0; i < recv_size; i++)
				std::cout << recv_buffer[i];
			nothing_recv = false;
		}
		io->writeToFile(RND_FILE, recv_buffer, recv_size, true);
	}

	sleep(2);
	if(recv_buffer != NULL)
		delete recv_buffer;

	delete send_buffer;
	mq_close(mqc);
	mq_close(mqs);

}



int main(int argc, char** argv)
{
	int code = 0;
	int new_count = 0;
	io = new IOUnit();


	code = readParameters(argc, argv, &new_count);

	if((TCP_CODE & code) == TCP_CODE)
	{
		testTcp(new_count, argv);
	}
	if((UDP_CODE & code) == UDP_CODE)
	{
		testUdp(new_count, argv);
	}
	if((UDS_CODE & code) == UDS_CODE)
	{
		testUds(new_count, argv);
	}
	if((MQ_CODE & code) == MQ_CODE)
	{
		testMq(new_count, argv);
	}

	delete io;
	return 0;

}


